from typing import List, Optional

from common.logger import logger
from common.typing_utils import numeric

from common.geometry.geom import Geom
from common.geometry.bbox import BoundingBox
from common.geoimage.gdal_sys import GDALRaster, warp_ds
from common.geoimage.virtual_raster import VRTSceneCollection
from common.geoimage.raster_dataset import RasterDataset

from catalog.models.base_stac_item import BaseSTACItem
from catalog.raster.utils import (
    determine_dest_srs,
    determine_dest_res,
    determine_dest_extent,
)


def composite_recent(
    items: List[BaseSTACItem],
    asset_names: List[str],
    extent: Optional[BoundingBox] = None,
    aoi: Optional[Geom] = None,
    dest_srs: Optional[str] = None,
    dest_res: Optional[numeric] = None,
    resample_alg: str = "near",
):
    """Composite a given list of `BaseSTACItems` using the show-most-recent-data strategy

    NOTE: This function is designed to be used within `catalog.raster.raster` only.
    It relies on the caller to provide several arguments correctly.
    """
    items = sorted(items, key=lambda item: item.start_datetime, reverse=True)
    logger.debug(str(items))

    try:
        nodata_values = []
        for asset_name in asset_names:
            nodata_values.append(items[0].bands[asset_name].nodata_val)
        if len(nodata_values) == 1:
            nodata_values = nodata_values[0]
    except Exception:
        logger.warn(
            "cannot read `nodata_values` from given product's default settings, fall back to use `0`."
        )
        nodata_values = 0

    vrt_scenes = [
        item.choose_assets(asset_names, dest_nodata=nodata_values) for item in items
    ]
    if any(vs is None for vs in vrt_scenes):
        return None
    vrt_scene_collection = VRTSceneCollection(vrt_scenes)

    if dest_srs is None:
        dest_srs = determine_dest_srs(aoi, items)

    if dest_res is None:
        dest_res = determine_dest_res(dest_srs, items)

    dest_extent = determine_dest_extent(extent, aoi, dest_srs, dest_res)

    vrt_scene_collection_warped = vrt_scene_collection.warp_to_common_srs(
        dest_srs=dest_srs, resample_alg=resample_alg, dest_nodata=nodata_values
    )
    vrt_scene = vrt_scene_collection_warped.mosaic_vrt()
    with GDALRaster.open(vrt_scene._vrt_file_path) as gdal_rst:
        warped_ds = warp_ds(
            gdal_rst,
            dest_srs=dest_srs,
            dest_extent=dest_extent,
            dest_nodata=nodata_values,
            dest_res=dest_res,
            resample_alg=resample_alg,
        )
        with warped_ds:
            rds = RasterDataset.from_gdal_raster(warped_ds, extent=dest_extent)

    return rds
